#include "sema.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Casting.h"

// 符号声明节点
std::shared_ptr<ASTNode> Sema::SemaVariableDeclNode(Token token, CType *ty)
{
    llvm::StringRef text(token.ptr, token.length);
    auto symbol = scope.FindVarSymbolInCurEnv(text);
    if (symbol) // 查找是否重定义
    {
        GetDiaEngine().Report(llvm::SMLoc::getFromPointer(token.ptr),
                              diag::err_redefine,
                              llvm::StringRef(token.ptr, token.length));
    }

    scope.AddVarSymbol(SymbolKind::LocalVariable, ty, text); // 添加到符号表

    auto variableDecl = std::make_shared<VariableDecl>();
    variableDecl->token = token;
    variableDecl->type = ty;

    return variableDecl;
}

std::shared_ptr<ASTNode> Sema::SemaIfStmtNode(std::shared_ptr<ASTNode> condNode, 
                                              std::shared_ptr<ASTNode> thenNode, 
                                              std::shared_ptr<ASTNode> elseNode)
{
    auto ifStmt = std::make_shared<IfStmt>();
    ifStmt->condNode = condNode;
    ifStmt->thenNode = thenNode;
    ifStmt->elseNode = elseNode;

    return ifStmt;
}

std::shared_ptr<ASTNode> Sema::SemaVariableAccessNode(Token token)
{
    llvm::StringRef text(token.ptr, token.length);
    auto symbol = scope.FindVarSymbol(text);
    // auto symbol = scope.FindVarSymbolInCurEnv(name); // err
    if (symbol == nullptr)
    {
        GetDiaEngine().Report(llvm::SMLoc::getFromPointer(token.ptr),
                              diag::err_undefine,
                              llvm::StringRef(token.ptr, token.length));
    }
    auto varAcc = std::make_shared<VariableAccessExpr>();
    varAcc->token = token;
    varAcc->type = symbol->getTy();

    return varAcc;
}

std::shared_ptr<ASTNode> Sema::SemaAssignExprNode(std::shared_ptr<ASTNode> left, std::shared_ptr<ASTNode> right, Token token)
{
    assert(left && right);

    if (!llvm::isa<VariableAccessExpr>(left.get()))
    {
        diagEngine.Report(llvm::SMLoc::getFromPointer(left->token.ptr), diag::err_lvalue);
    }

    auto assign = std::make_shared<AssignExpr>();
    assign->left = left;
    assign->right = right;

    return assign;
}

std::shared_ptr<ASTNode> Sema::SemaBinaryExprNode(std::shared_ptr<ASTNode> left, std::shared_ptr<ASTNode> right, OpCode op)
{
    auto binaryExpr = std::make_shared<BinaryExpr>();
    binaryExpr->left = left;
    binaryExpr->right = right;
    binaryExpr->op = op;

    return binaryExpr;
}

std::shared_ptr<ASTNode> Sema::SemaNumberExprNode(Token token, CType *ty)
{
    auto factorExpr = std::make_shared<NumberExpr>();
    factorExpr->token = token;
    factorExpr->type = ty;

    return factorExpr;
}

void Sema::EnterScope()
{
    scope.EnterScope();
}
void Sema::ExitScope()
{
    scope.ExitScope();
}